// $Id: oaxaca.c 407 2014-03-06 05:31:08Z alan.watson.f $

#include "oaxaca-config.h"
#include "oaxaca.h"
#include "mprintf.h"

#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>

////////////////////////////////////////////////////////////////////////////////

static const char *process_name = "";
static FILE *logfp = NULL;

static char *
timestamp(void)
{
  static char s[] = "HH:MM:SS";
  static char t[] = "HH:MM:SS.SSS";
  struct timeval tv;
  gettimeofday(&tv, NULL);
  struct tm *tm = localtime(&tv.tv_sec);
  strftime(s, sizeof(s), "%T", tm);
  sprintf(t, "%s.%03lu", s, (unsigned long) (tv.tv_usec / 1000));
  return t;
}

static const char *
owner(void)
{
  if (strcmp(process_name, "") != 0) {
    return process_name;
  } else {
    return "oaxaca";
  }
}

static void
log_message(const char *type, const char *message)
{
  char *line = mprintf("%s: %s: %s: %s\n", timestamp(), owner(), type, message);
  fputs(line, stderr);
  if (logfp != NULL) {
    fputs(line, logfp);
  }
  free(line);
}


void
oaxaca_debug(const char *format, ...)
{
  va_list ap;
  va_start(ap, format);
  char *message = vmprintf(format, ap);
  va_end(ap);
  log_message("debug", message);
  free(message);
}

void oaxaca_debug_simple(const char *message)
{
  oaxaca_debug("%s", message);
}

void
oaxaca_info(const char *format, ...)
{
  va_list ap;
  va_start(ap, format);
  char *message = vmprintf(format, ap);
  va_end(ap);
  log_message("info", message);
  free(message);
}

void oaxaca_info_simple(const char *message)
{
  oaxaca_info("%s", message);
}

void
oaxaca_error(const char *format, ...)
{
  va_list ap;
  va_start(ap, format);
  char *message = vmprintf(format, ap);
  va_end(ap);
  log_message("error", message);
  free(message);
  oaxaca_exit(1);
}

void oaxaca_error_simple(const char *message)
{
  oaxaca_error("%s", message);
}

////////////////////////////////////////////////////////////////////////////////

volatile int *oaxaca_initialized = NULL;

static int expectedexit = 0;

void
oaxaca_exit(int status)
{
  expectedexit = 1;
  exit(status);
}

static void
atexitcallee(void)
{
  if (!expectedexit)
    oaxaca_info("error: unexpected exit.");
}

static void
signalexithandler(int signal)
{
  exit(1);
}

void
oaxaca_common_main(const char *name)
{
  atexit(atexitcallee);
  
  // See man 7 signal.
  signal(SIGHUP,    signalexithandler);
  signal(SIGINT,    signalexithandler);
  signal(SIGQUIT,   signalexithandler);
  signal(SIGILL,    signalexithandler);
  signal(SIGABRT,   signalexithandler);
  signal(SIGFPE,    signalexithandler);
  signal(SIGSEGV,   signalexithandler);
  signal(SIGPIPE,   signalexithandler);
  signal(SIGALRM,   signalexithandler);
  signal(SIGTERM,   signalexithandler);
  signal(SIGUSR1,   signalexithandler);
  signal(SIGUSR2,   signalexithandler);
  signal(SIGBUS,    signalexithandler);
  signal(SIGPOLL,   signalexithandler);
  signal(SIGPROF,   signalexithandler);
  signal(SIGSYS,    signalexithandler);
  signal(SIGTRAP,   signalexithandler);
  signal(SIGVTALRM, signalexithandler);
  signal(SIGXCPU,   signalexithandler);
  signal(SIGXFSZ,   signalexithandler);
  signal(SIGIOT,    signalexithandler);
  signal(SIGSTKFLT, signalexithandler);
  signal(SIGIO,     signalexithandler);
  signal(SIGPWR,    signalexithandler);
  
  // Set the process name to oaxaca-name.

  process_name = mprintf("oaxaca-%s", name);
  if (process_name == NULL)
    oaxaca_error("unable to allocate memory for the process name.");
  if (prctl(PR_SET_NAME, (unsigned long) process_name, 0, 0, 0) == -1)
    oaxaca_error("unable to set process name.");
  
  const char *oaxacadir = getenv("OAXACADIR");
  if (oaxacadir == NULL)
    oaxaca_error("unable determine $OAXACADIR.");
  
  char *logfile = mprintf("%s/var/log/oaxaca.txt", oaxacadir);
  logfp = fopen(logfile, "a");
  if (logfp == NULL)
    oaxaca_error("unable to open \"%s\".", logfile);
  setvbuf(logfp, NULL, _IOLBF, 0);
  free(logfile);

  oaxaca_info("starting.");

  // Change directory to $OAXACADIR/var/name.
  
  const mode_t directory_mode = 
  	(S_IRUSR|S_IWUSR|S_IXUSR)|(S_IRGRP|S_IXGRP)|(S_IROTH|S_IXOTH);
  
  if (chdir(oaxacadir) == -1)
    oaxaca_error("unable to change directory to \"%s\".", oaxacadir);
  
  if (chdir("var") == -1) {
    if (mkdir("var", directory_mode) == -1)
      oaxaca_error("unable to create directory \"%s/var/\".", oaxacadir);
    if (chdir("var") == -1)
      oaxaca_error("unable to change directory to \"%s/var/\".", oaxacadir);
  }

  if (chdir(name) == -1) {
    if (mkdir(name, directory_mode) == -1)
      oaxaca_error("unable to create directory \"%s/var/%s\".", oaxacadir, name);
    if (chdir(name) == -1)
      oaxaca_error("unable to change directory to \"%s/var/%s\".", oaxacadir, name);
  }

  fclose(stdin);

  oaxaca_info("initializing shared memory.");
  const char *message;
  message = oaxaca_shm_initialize();
  if (message != NULL)
    oaxaca_error(message);
    
}

////////////////////////////////////////////////////////////////////////////////

const char *
oaxaca_initialize(void)
{

  // Change process name.
  process_name = "oaxaca";
  if (prctl(PR_SET_NAME, (unsigned long) process_name, 0, 0, 0) == -1)
    return "unable to set process name.";

  const char *oaxacadir = getenv("OAXACADIR");
  if (oaxacadir == NULL)
    oaxaca_error("unable determine $OAXACADIR.");
  
  char *logfile = mprintf("%s/var/log/oaxaca.txt", oaxacadir);
  logfp = fopen(logfile, "a");
  if (logfp == NULL)
    oaxaca_error("unable to open \"%s\".", logfile);
  setvbuf(logfp, NULL, _IOLBF, 0);
  free(logfile);

  const char *s = oaxaca_shm_initialize();
  if (s != NULL)
    return s;

  return "";
}

////////////////////////////////////////////////////////////////////////////////
